import * as Koa from 'koa';
import Router = require('koa-router');
import bodyParser = require("koa-bodyparser");
import { cors, pipe } from "../middleware";
import { Logger } from '../utils';
import http = require("http");
const convert = require('koa-convert');
const logger = require('koa-logger');

export class Server {
    static __Routers: Map<{ target: any, method: string, path: string }, Function | Function[]> = new Map();
    private router: Router
    private app: Koa

    constructor() {
        this.app = new Koa();
        this.router = new Router();
        this.init();
    }

    /**
     * 初始化方法
     * 顺序必须为:
     * (1)Parser
     * (2)MiddleWares
     * (3)Routers
     */
    private init() {
        this.registerParser();
        this.registerMiddleWares();
        this.registerRouters();
    }

    /**
     * http通信协议
     * (1)json
     * (2)form
     * (3)text
     */
    private registerParser() {
        this.app.use(bodyParser({
            extendTypes: {
                json: ['application/json'],
                form: ['application/x-www-form-urlencoded'],
                text: ['text/plain']
            },
            strict: false,
            onerror: (err: any, ctx) => {
                err.status = err.status || 422;
                err.body = err.body ? "请求数据有误！请求格式为：" + err.body : "请求数据有误！";
                let e = {
                    "code": err.status,
                    "msg": err.body
                };
                ctx.throw(JSON.stringify(e), err.status);
            }
        }));
    }

    /**
     * 挂载中间件
     * cors：跨域
     * pipe：异常处理
     * logger：日志
     */
    private registerMiddleWares() {
        this.app.use(convert.compose(
            cors(),
            pipe(),
            logger()
        ));
    }

    /**
     * 挂载路由
     */
    private registerRouters() {
        Server.__Routers.forEach((controller, config) => {
            let controllers = Array.isArray(controller) ? controller : [controller];
            controllers.forEach((controller) => this.router[config.method](config.path, controller.bind(config.target)));
        })
        this.app.use(this.router.routes())
        this.app.use(this.router.allowedMethods())
    }

    /**
     * 启动服务
     * 默认端口：8899
     */
    listen(port: number) {
        let server = http.createServer(this.app.callback());
        require('../cns')(server);
        server.listen(port, ()=> {
            console.log('CNS服务器启动成功，监听端口： %d, in %s mode', port, this.app.env);
        });
    }
}